// Serial Communication Firmware

#include "megaBrite.h"
#include "Metro.h"

#define __VERBOSE__           // Comment out to prevent message printing back to the computer

// Constants
#define MAX_PLAYERS     16    // Max amount of megaBrites allowed (protocol's hard limit)
#define DEFAULT_TIMEOUT 10    // Default polling interval
#define CHAN_MSK        0x78  // Channel number bitmask (when parsing channel-wide headers)
#define HNDSHK_CHAR     '.'   // Character used when handshaking (echo)

// Global Variables
byte state = 8;    // Ready to receive a new header
byte header;
byte timerFlag = 0;
byte updateFlag = 0;
unsigned int red,green,blue,smoothTime;

// Global objects 
megaBrite lights = megaBrite(1);
Metro timer = Metro(DEFAULT_TIMEOUT, 1);

// Function prototypes
void process(byte input);
void handshake();

void setup() {
    // Init Communications
    Serial.begin(115200); 
    lights.update();
    handshake();
}


void loop() {
    if ( Serial.available() ) process( Serial.read() );  //Serial comm
    if ( timer.check() && timerFlag ) lights.update();   //Lights update timer
}


void process(byte input) {
    state++;  // There're 8 possible states, activated if the correspondant header flag is set
    /*
    #ifdef __VERBOSE__
        Serial.print("STATE: ");
        Serial.println(state,DEC);
    #endif
    */
    switch (state){
        case 1:
        // RGB Parsing
            if( bitRead(header,2) ){
                red = (unsigned int)(input);
            }
            else process(input);
            break;
        case 2:
            if( bitRead(header,2) ){
                red += (unsigned int)(input & 0x03) << 8;
                green = (unsigned int)(input & 0xFC) >> 2;
            }
            else process(input);
            break;
        case 3:
            if( bitRead(header,2) ){
                green += (unsigned int)(input & 0x0F) << 6;
                blue = (unsigned int)(input & 0xF0) >> 4;
            }
            else process(input);
            break;
        case 4:
            if( bitRead(header,2) ){
                blue += (unsigned int)(input & 0x3F) << 4;
                // Update color
                if( bitRead(header,7) ) lights.channels[(header & CHAN_MSK) >> 3].setRGB(red,green,blue);
                else lights.setRGB(red,green,blue);
                #ifdef __VERBOSE__
                    Serial.print("RGB: ");
                    Serial.print(red);
                    Serial.print(" ");
                    Serial.print(green);
                    Serial.print(" ");
                    Serial.println(blue);
                #endif
                if( updateFlag ) lights.update();
            }
            else process(input);
            break;
        case 5:
        // SmoothTime parsing
            if( bitRead(header,1) ){
                smoothTime = (unsigned int)(input);
            }
            else process(input);
            break;
        case 6:
            if( bitRead(header,1) ){
                smoothTime += (unsigned int)(input) << 8;
                // Update SmoothTime
                if( bitRead(header,7) ){
                    lights.channels[(header & CHAN_MSK) >> 3].setSmoothTime(smoothTime);
                }
                else lights.setSmoothTime(smoothTime);
                #ifdef __VERBOSE__
                    Serial.print("ST: ");
                    Serial.println(smoothTime);
                #endif
            }
            else process(input);
            break;
        case 7:
        // NChannels parsing & updating
            if( (!bitRead(header,7) ) && bitRead(header,3) ){
                lights.setChannels(input & 0x0F);
                #ifdef __VERBOSE__
                    Serial.print("CHAN: ");
                    Serial.println(input & 0x0F);
                #endif                
            }
            else process(input);
            break;
        case 8:
        // Polling time parsing & updating
            if( (!bitRead(header,7) ) && bitRead(header,4) ){
                timer.interval(input);
                #ifdef __VERBOSE__
                    Serial.print("POLL: ");
                    Serial.println(input,DEC);
                #endif
            }
            else process(input);
            break;            
        default:
        // New message
            // Assign hedaer
            header = input;
            #ifdef __VERBOSE__
                Serial.print("HEADER: ");
                Serial.println(header,BIN);
            #endif
            // Header parsing
            if( bitRead(header,7) ){
                lights.channels[(header & CHAN_MSK) >> 3].setMode( bitRead(header,0) );
            }
            else{
                lights.setMode( bitRead(header,0) );
                timerFlag = bitRead(header,5);
                if ( bitRead(header,6) ){
                    if( bitRead(header,2) ) updateFlag = 1;
                    else lights.update();
                }
            }
            // Get ready to receive body of message
            state = 0;
            break;
    }
}

void handshake(){
    byte feedback;
    do{
        Serial.print(HNDSHK_CHAR);  
        delay(300);
        if ( Serial.available() ) feedback = Serial.read();
    } while (feedback != HNDSHK_CHAR);
    #ifdef __VERBOSE__
        Serial.println("Hello, World!");
    #endif
}
